package com.clss.webrtclibrary;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.GridLayout;

import org.webrtc.EglBase;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;

import java.util.HashMap;
import java.util.Map;

import static com.clss.webrtclibrary.KurentoServer.WEBRTC_TYPE_COMMAND;
import static com.clss.webrtclibrary.KurentoServer.WEBRTC_TYPE_COMMAND_LEADER;

/**
 * 应急视频房间
 *
 * @author liuv
 * @date 2018/5/8
 */

public class CommandVideoRoom implements OnItemSurfaceViewVoiceSwitchListener {
    private static final String TAG = "CommandVideoRoom";

    private static final String FULL_TAG = "fullTag";

    private static final int[][] FOUR_GRID_PARAMS = {
            {0, 0}, {0, 3},
            {3, 0}, {3, 3}};
    private static final int[][] NINE_GRID_PARAMS = {
            {0, 0}, {0, 2}, {0, 4},
            {2, 0}, {2, 2}, {2, 4},
            {4, 0}, {4, 2}, {4, 4}};
    private static final int GRID_LINE_COUNT = 6;

    private MediaInfoBean mMediaInfoBean;
    private Context mContext;
    private KurentoServer mKurentoServer;
    private KurentoMediaCallback mKurentoMediaCallback;
    private static HashMap<String, MediaStream> mRemoteStreamMap = new HashMap<>();
    private HashMap<String, ItemSurfaceView> mRemoteSurfaceViewMap = new HashMap<>();
    private HashMap<String, ProxyVideoSink> mRemoteProxyVideoSinkMap = new HashMap<>();
    private EglBase mEglBase;
    private ProxyVideoSink mMainProxyVideoSink = new ProxyVideoSink();
    private ProxyVideoSink mFullProxyVideoSink = new ProxyVideoSink();
    private SurfaceViewRenderer mMainRenderer, mFullRenderer;
    private String mFullName;
    private IVideoRoomListener mIVideoRoomListener;
    private MediaStream mLocalStream;
    private FrameLayout mFrameLayout;
    private GridLayout mGridLayout;
    private Handler mHandler;
    private ItemSurfaceView mMainItemSurfaceView;
    private int mGridHight, mGridWidth, mGridLineHoldCount = 3;
    private int mCommandType;
    private boolean isPublishing, isShowLocalVideoInRemote = true;
    private String mClickUserId;

    public CommandVideoRoom(MediaInfoBean mediaInfoBean, FrameLayout frameLayout,
                            IVideoRoomListener iVideoRoomListener, Handler handler,
                            Boolean isLeader,String clickUserId) {
        this.mMediaInfoBean = mediaInfoBean;
        this.mIVideoRoomListener = iVideoRoomListener;
        this.mContext = mIVideoRoomListener.getContext();
        this.mEglBase = EglBase.create();
        this.mFrameLayout = frameLayout;
        this.mHandler = handler;
        this.mCommandType = isLeader ? WEBRTC_TYPE_COMMAND_LEADER : WEBRTC_TYPE_COMMAND;
        this.isPublishing = !isLeader;
        this.mClickUserId = clickUserId;
        initLayout();
        if(!isLeader) {
            addLayoutParams();
        }
        startVideo();
        Activity activity = (Activity) mContext;
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    private void initLayout() {
        mGridHight = mFrameLayout.getMeasuredHeight();
        mGridWidth = mFrameLayout.getMeasuredWidth();
        Lg.i(TAG, "---mGridHight===" + mGridHight);
        Lg.i(TAG, "---mGridWidth===" + mGridWidth);
        mGridLayout = new GridLayout(mContext);
        mGridLayout.setRowCount(GRID_LINE_COUNT);
        mGridLayout.setColumnCount(GRID_LINE_COUNT);
        mFrameLayout.addView(mGridLayout);
        mMainItemSurfaceView = new ItemSurfaceView(mContext, mEglBase, "", "", this);
        mMainRenderer = mMainItemSurfaceView.getSurfaceViewRenderer();
        mMainProxyVideoSink.setTarget(mMainRenderer);
    }

    private void addLayoutParams() {
        GridLayout.Spec rowSpec = GridLayout.spec(FOUR_GRID_PARAMS[0][0], mGridLineHoldCount);
        GridLayout.Spec columnSpec = GridLayout.spec(FOUR_GRID_PARAMS[0][1], mGridLineHoldCount);
        GridLayout.LayoutParams mLayoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
        mGridLayout.addView(mMainItemSurfaceView,0, mLayoutParams);
    }

    private void startVideo() {
        mKurentoServer = new KurentoServer(mContext, mCommandType,
                new KurentoGatewayCallback());
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        mKurentoServer.initializeMediaContext(options);
    }

    public boolean stopVideo() {
        if (mRemoteStreamMap.size() > 0) {
            Lg.i(TAG, "---mRemoteStreamMap.size() > 0===" + mRemoteStreamMap.size());
            mRemoteStreamMap.clear();
        }
        if (mFullProxyVideoSink != null) {
            mFullProxyVideoSink.setTarget(null);
        }
        for (Map.Entry<String, ProxyVideoSink> entry : mRemoteProxyVideoSinkMap.entrySet()) {
            entry.getValue().setTarget(null);
        }
        if (mMainProxyVideoSink != null) {
            mMainProxyVideoSink.setTarget(null);
        }
        if (mFullRenderer != null) {
            mFullRenderer.release();
            mFullRenderer = null;
        }
        for (Map.Entry<String, ItemSurfaceView> entry : mRemoteSurfaceViewMap.entrySet()) {
            entry.getValue().getSurfaceViewRenderer().release();
            entry.setValue(null);
        }
        if (mMainRenderer != null) {
            mMainRenderer.release();
            mMainRenderer = null;
        }
        mFullProxyVideoSink = null;
        mMainProxyVideoSink = null;
        mEglBase.release();
        mFrameLayout.removeAllViews();
        return mKurentoServer.closeAllLiveVideo();
    }

    public boolean switchCamera() {
        return mKurentoServer.switchCamera();
    }

    public AppRTCAudioManager.AudioDevice switchSoundOutput(){
        return mKurentoServer.switchSoundOutput();
    }

    public void switchAllRemoteSilence(boolean isSilence){
            for (Map.Entry<String, ItemSurfaceView> entry : mRemoteSurfaceViewMap.entrySet()) {
                if (entry.getValue().getVoiceSwitchCb().isChecked() == isSilence) {
                    entry.getValue().getVoiceSwitchCb().performClick();
                }
            }
    }

    private void setCacheRemoteStreamDisable(String id) {
        Lg.i(TAG, "---setCacheRemoteStreamDisable===" + (mRemoteStreamMap.size()));
        if (mRemoteStreamMap.containsKey(id)) {
            if (mRemoteStreamMap.get(id).videoTracks.size() > 0 && mRemoteStreamMap.get(id).audioTracks.size() > 0) {
                mRemoteStreamMap.get(id).audioTracks.get(0).setEnabled(false);
            }
        }
    }

    private void setCacheRemoteStreamEnable(String id) {
        Lg.i(TAG, "---setCacheRemoteStreamEnable===" + (mRemoteStreamMap.size()));
        if (mRemoteStreamMap.containsKey(id)) {
            if (mRemoteStreamMap.get(id).videoTracks.size() > 0 && mRemoteStreamMap.get(id).audioTracks.size() > 0) {
                mRemoteStreamMap.get(id).audioTracks.get(0).setEnabled(true);
            }
        }
    }

    private void videoStart() {
        if (mIVideoRoomListener != null) {
            mIVideoRoomListener.videoOnStart();
        }
    }

    private GridLayout.LayoutParams getGridLayoutParams() {
        GridLayout.Spec rowSpec, columnSpec;
        int childCount = mGridLayout.getChildCount();
        Lg.i(TAG, "---getGridLayoutParams===" + childCount);
        if (childCount < 4) {
            mGridLineHoldCount = 3;
            rowSpec = GridLayout.spec(FOUR_GRID_PARAMS[childCount][0], mGridLineHoldCount);
            columnSpec = GridLayout.spec(FOUR_GRID_PARAMS[childCount][1], mGridLineHoldCount);
            return new GridLayout.LayoutParams(rowSpec, columnSpec);
        } else {
            mGridLineHoldCount = 2;
            rowSpec = GridLayout.spec(NINE_GRID_PARAMS[childCount][0], mGridLineHoldCount);
            columnSpec = GridLayout.spec(NINE_GRID_PARAMS[childCount][1], mGridLineHoldCount);
            return new GridLayout.LayoutParams(rowSpec, columnSpec);
        }
    }

    private void refreshGridLayout() {
        int childCount = mGridLayout.getChildCount();
        Lg.i(TAG, "---refreshGridLayout===" + childCount);
        mGridLineHoldCount = childCount < 5 ? 3 : 2;
        GridLayout.Spec rowSpec, columnSpec;
        for (int index = 0; index < childCount; index++) {
            ItemSurfaceView itemSurfaceView = (ItemSurfaceView) mGridLayout.getChildAt(index);
            itemSurfaceView.modifyViewSize(mGridHight / GRID_LINE_COUNT * mGridLineHoldCount,
                    mGridWidth / GRID_LINE_COUNT * mGridLineHoldCount);
            if (mGridLineHoldCount == 3) {
                rowSpec = GridLayout.spec(FOUR_GRID_PARAMS[index][0], mGridLineHoldCount);
                columnSpec = GridLayout.spec(FOUR_GRID_PARAMS[index][1], mGridLineHoldCount);
                itemSurfaceView.setLayoutParams(new GridLayout.LayoutParams(rowSpec, columnSpec));
            } else if (mGridLineHoldCount == 2) {
                rowSpec = GridLayout.spec(NINE_GRID_PARAMS[index][0], mGridLineHoldCount);
                columnSpec = GridLayout.spec(NINE_GRID_PARAMS[index][1], mGridLineHoldCount);
                itemSurfaceView.setLayoutParams(new GridLayout.LayoutParams(rowSpec, columnSpec));
            }
        }
    }

    private void addRemoteSurfaceView(String id, MediaStream mediaStream) {
        Lg.i(TAG, "---addRemoteSurfaceView===" + id);
        String name = mKurentoServer.getUnitAndNameFromId(id);
        mHandler.post(() -> {
            if(id.equals(mClickUserId)){
                setSurfaceViewToFull(id);
            }
            ItemSurfaceView itemSurfaceView = new ItemSurfaceView(mContext, mEglBase, name, id, this);
            itemSurfaceView.modifyViewSize(mGridHight / GRID_LINE_COUNT * mGridLineHoldCount,
                    mGridWidth / GRID_LINE_COUNT * mGridLineHoldCount);
            mGridLayout.addView(itemSurfaceView, getGridLayoutParams());
            mRemoteSurfaceViewMap.put(id, itemSurfaceView);
            ProxyVideoSink proxyVideoSink = new ProxyVideoSink();
            proxyVideoSink.setTarget(itemSurfaceView.getSurfaceViewRenderer());
            mediaStream.videoTracks.get(0).addSink(proxyVideoSink);
            mRemoteProxyVideoSinkMap.put(id, proxyVideoSink);
            refreshGridLayout();
        });
    }

    private void removeRemoteSurfaceView(String id) {
        mHandler.post(() -> {
            for (int index = 0; index < mGridLayout.getChildCount(); index++) {
                Lg.i(TAG, "---removeRemoteSurfaceView===" + mGridLayout.getChildAt(index).getTag());
                if (mGridLayout.getChildAt(index).getTag().equals(id)) {
                    mGridLayout.removeViewAt(index);

                    if (mRemoteProxyVideoSinkMap.containsKey(id)) {
                        mRemoteProxyVideoSinkMap.get(id).setTarget(null);
                        mRemoteProxyVideoSinkMap.remove(id);
                    }
                    if (mRemoteSurfaceViewMap.containsKey(id)) {
                        mRemoteSurfaceViewMap.get(id).getSurfaceViewRenderer().release();
                        mRemoteSurfaceViewMap.remove(id);
                    }
                    if (mRemoteStreamMap.containsKey(id)) {
                        mRemoteStreamMap.get(id).dispose();
                        mRemoteStreamMap.remove(id);
                    }
                }
            }
            Lg.i(TAG, "---removeRemoteSurfaceView===" + mGridLayout.getChildCount());
            refreshGridLayout();
        });
    }

    private void setSurfaceViewToFull(String id) {
        Lg.i(TAG, "---setSurfaceViewToFull===" + id);
        for (int index = 0; index < mFrameLayout.getChildCount(); index++) {
            if (FULL_TAG.equals(mFrameLayout.getChildAt(index).getTag())) {
                mFullProxyVideoSink.setTarget(null);
                mFrameLayout.removeViewAt(index);
                if (TextUtils.isEmpty(mFullName)) {
                    mLocalStream.videoTracks.get(0).removeSink(mFullProxyVideoSink);
                } else {
                    if (mRemoteStreamMap.containsKey(mFullName)) {
                        mRemoteStreamMap.get(mFullName).videoTracks.get(0).removeSink(mFullProxyVideoSink);
                    }
                }
                mFullRenderer.release();
                mFullRenderer = null;
                mFullProxyVideoSink = null;
                return;
            }
        }

        mFullRenderer = new SurfaceViewRenderer(mContext);
        mFullRenderer.setZOrderOnTop(true);
        mFullRenderer.init(mEglBase.getEglBaseContext(), null);
        mFullRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
        mFullRenderer.setEnableHardwareScaler(true);
        mFullRenderer.setTag(FULL_TAG);
        mFullRenderer.setOnClickListener(v -> quitFullSurfaceView());

        mFullProxyVideoSink = new ProxyVideoSink();
        mFullProxyVideoSink.setTarget(mFullRenderer);

        if (TextUtils.isEmpty(id)) {
            if (mLocalStream != null && isPublishing) {
                mLocalStream.videoTracks.get(0).addSink(mFullProxyVideoSink);
            } else {
                return;
            }
        } else {
            if (mRemoteStreamMap.containsKey(id)) {
                mRemoteStreamMap.get(id).videoTracks.get(0).addSink(mFullProxyVideoSink);
            }
        }
        mFrameLayout.addView(mFullRenderer);
        mFullName = id;
    }

    private void quitFullSurfaceView() {
        for (int index = 0; index < mFrameLayout.getChildCount(); index++) {
            if (FULL_TAG.equals(mFrameLayout.getChildAt(index).getTag())) {
                mFullProxyVideoSink.setTarget(null);
                mFrameLayout.removeViewAt(index);
            }
        }
        if (TextUtils.isEmpty(mFullName)) {
            if (mLocalStream != null && isPublishing) {
                mLocalStream.videoTracks.get(0).removeSink(mFullProxyVideoSink);
            }
        } else {
            if (mRemoteStreamMap.containsKey(mFullName)) {
                mRemoteStreamMap.get(mFullName).videoTracks.get(0).removeSink(mFullProxyVideoSink);
            }
        }
        mFullRenderer.release();
        mFullRenderer = null;
        mFullProxyVideoSink = null;
    }

    @Override
    public void itemSurfaceViewIsChanged(boolean isOpen, String id) {
        Lg.i(TAG, "---itemSurfaceViewIsChanged===" + isOpen + "----" + id);
        if (isOpen) {
            setCacheRemoteStreamEnable(id);
        } else {
            setCacheRemoteStreamDisable(id);
        }
    }

    @Override
    public void itemSurfaceViewOnClick(String id) {
        setSurfaceViewToFull(id);
    }

    @Override
    public void itemSurfaceViewOnLongClick(String id, String name) {
        //应急领导可以移除视频
        if (mCommandType != WEBRTC_TYPE_COMMAND_LEADER) {
            return;
        }
        //领导不能移除自己的视频，可以不发布本地视频
        if (id.equals(mMainItemSurfaceView.getTag())) {
            return;
        }
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
        alertDialog.setTitle("是否屏蔽" + name + "？");
        alertDialog.setPositiveButton("是", (dialog, which) -> {
            dialog.dismiss();
            mKurentoServer.removeInPublisherList(id);
            removeRemoteSurfaceView(id);
        });
        alertDialog.setNegativeButton("否", (dialog, which) -> {
            dialog.dismiss();
        });
        alertDialog.show();
    }

    /** 收到新的信令 */
    public void receivedNewSign(KurentoSignallingBean receivedBean){
        if (mKurentoServer != null){
            mKurentoServer.receivedNewMessage(receivedBean);
        }
    }

    /**
     * 选择要加入的视频
     */
    public void showWaitPublisherListDialog() {
        mKurentoServer.showWaitPublisherListDialog();
    }

    /**
     * 领导开始发布自己的视频
     * 默认不发布本机的图像
     */
    public boolean joinVideoRoom() {
        isShowLocalVideoInRemote = !(mCommandType == WEBRTC_TYPE_COMMAND_LEADER);
        if (!mKurentoServer.showMaxVideoToast()) {
            return false;
        }

        if (mKurentoServer != null && !isPublishing) {
            mKurentoServer.commandLeaderJoinRoom();
            isPublishing = true;
        }
        return true;
    }

    /**
     * 是否发布本机的图像
     * @param isEnable
     */
    public boolean setOrRemoveImageEnable(boolean isEnable) {
        if (isEnable) {
            //发布之前判断能不能加入视频
            if (mKurentoServer == null || !isPublishing) {
                Lg.i(TAG, "setOrRemoveImageEnable: mKurentoServer == null "+isPublishing);
                return !isEnable;
            }
            if (mKurentoServer.showMaxVideoToast()) {
                if (mLocalStream != null && isPublishing) {
                    mLocalStream.videoTracks.get(0).setEnabled(isEnable);
                }
                isShowLocalVideoInRemote = true;
                addLayoutParams();
                refreshGridLayout();
                return isEnable;
            } else {
                isShowLocalVideoInRemote = false;
                //加入失败
                Lg.i(TAG, "setOrRemoveImageEnable: showMaxVideoToast");
                return !isEnable;
            }
        } else {
            if (mLocalStream != null && isPublishing) {
                mLocalStream.videoTracks.get(0).setEnabled(isEnable);
            }
            isShowLocalVideoInRemote = false;
            mGridLayout.removeViewAt(0);
            refreshGridLayout();
        }
        return isEnable;
    }

    /**
     * 是否发布本机的图像
     *
     * @param isEnable
     */
    public void setPublishImageEnable(boolean isEnable) {
        if (mLocalStream != null && isPublishing) {
            mLocalStream.videoTracks.get(0).setEnabled(isEnable);
        }
    }

    /**
     * 是否发布本机的声音
     *
     * @param isEnable
     */
    public void setPublishAudioEnable(boolean isEnable) {
        if (mLocalStream != null && isPublishing) {
            mLocalStream.audioTracks.get(0).setEnabled(isEnable);
        }
    }

    /**
     * 网关回调
     */
    private class KurentoGatewayCallback implements IKurentoGatewayCallback {

        @Override
        public MediaInfoBean getMediaInfo() {
            return mMediaInfoBean;
        }

        @Override
        public IKurentoMediaCallbacks getMediaHandleCallback() {
            return mKurentoMediaCallback == null ?
                    mKurentoMediaCallback = new KurentoMediaCallback()
                    : mKurentoMediaCallback;
        }

        @Override
        public EglBase getEglBase() {
            return mEglBase;
        }

        @Override
        public IVideoRoomListener getVideoRoomListener() {
            return mIVideoRoomListener;
        }

        @Override
        public void webSocketDisconnect() {
            Lg.i(TAG, "---webSocketDisconnect===");
            if (mIVideoRoomListener != null) {
                mIVideoRoomListener.videoRoomDisconnect();
            }
        }

        @Override
        public void signalingParamError(String error) {
            if (mIVideoRoomListener != null) {
                mIVideoRoomListener.videoError(error);
            }
        }

        @Override
        public void arrivedRoom(KurentoSignallingBean signallingBean) {

        }

        @Override
        public void leaveRoom(String id) {
            removeRemoteSurfaceView(id);
        }

        @Override
        public void showConnectionInfo(String info) {
            Lg.i(TAG, "---showConnectionInfo===" + info);
            if (ConfigAndUtils.FAILED.equals(info.split(":")[1])) {
                Message msg = new Message();
                msg.obj = ConfigAndUtils.FAILED;
                mHandler.sendMessage(msg);
            }
        }

        @Override
        public Boolean getVideoTrackEnable() {
            return isShowLocalVideoInRemote;
        }

    }

    /**
     * media回调
     */
    public class KurentoMediaCallback implements IKurentoMediaCallbacks {

        @Override
        public void success(KurentoPeerManager handle) {

        }

        @Override
        public void onLocalStream(MediaStream stream) {
            Lg.i(TAG, "---onLocalStream===");
            videoStart();
            mLocalStream = stream;
            stream.videoTracks.get(0).addSink(mMainProxyVideoSink);
            mHandler.post(() -> {
                mMainItemSurfaceView.modifyViewSize(mGridHight, mGridWidth);
                refreshGridLayout();
            });
        }

        @Override
        public void onRemoteStream(MediaStream stream, String id) {
            Lg.i(TAG, "---onRemoteStream===" + id);
            videoStart();
            if (TextUtils.isEmpty(id)) {
                /* 发布视频时的回调中id为null，接收时不用处理后边的内容 */
                return;
            }
            if (stream == null) {
                return;
            }
            mRemoteStreamMap.put(id, stream);
            addRemoteSurfaceView(id, stream);
        }

        @Override
        public void onRemoveRemoteStream(String id) {
            Lg.i(TAG, "---onRemoveRemoteStream===" + id);
            setCacheRemoteStreamDisable(id);
            mRemoteStreamMap.remove(id);
        }

        @Override
        public void onDetached() {

        }

        @Override
        public Intent getScreenIntent() {
            return null;
        }

        @Override
        public void enableVideo(String id) {
            Lg.i(TAG, "---enableVideo===" + id);
        }

        @Override
        public void disableVideo(String id) {
            Lg.i(TAG, "---disableVideo===" + id);
        }
    }
}
